<?php

namespace App\Http\Controllers\Reservation;

use App\Http\Controllers\ApiController;
use App\Http\Requests\Reservation\ScheduleStoreRequest;
use App\Http\Requests\Reservation\ScheduleUpdateRequest;
use App\Models\Hospital;
use App\Models\Reservation\Reservation;
use App\Models\Reservation\Schedule;
use App\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class ScheduleController extends ApiController
{
    /**
     * 获取医生的预约安排
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function plan(Request $request)
    {
        $plan = $this->getSchedules($request, $request->input('times'));

        return $this->response($plan->keyBy('time')->toArray());
    }

    public function planInMonthAfterNow(Request $request)
    {
        $this->validate(
            $request,
            [
                'doctor_id' => 'integer',
                'time'      => 'date_format:Y-m-d',
            ],
            [
                'doctor_id.integer'   => '医生ID 必须是一个数字。',
                'time.date_format'    => '时间字段 必须是一个有效的日期, 且格式为：2017-08-01。',
//                'time.after_or_equal' => '时间字段的值 必须等于或晚于现在。',
            ]
        );

        $now  = Carbon::now();
        $time = Carbon::parse($request->input('time'));

        if ($now->month == $time->month) {
            $start = $time->day($now->day)->toDateString();
        } else {
            $start = $time->startOfMonth()->toDateString();
        }

        $end = $time->endOfMonth()->toDateString();

        $plan = $this->getSchedules($request, [$start, $end]);

        return $this->response($plan->keyBy('time')->toArray());
    }

    /**
     * 按月获取医生的预约安排
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function planOfMonth(Request $request)
    {
        $this->doctorAndTimeValidator($request);

        $time = $request->input('time');

        $plan = $this->getSchedules($request, $this->month($time));

        return $this->response($plan->keyBy('time')->toArray());
    }

    /**
     * @param \Illuminate\Http\Request $request
     * @param                          $times
     * @return \Illuminate\Database\Eloquent\Collection|static[]
     */
    protected function getSchedules(Request $request, $times)
    {
        $doctor_id = $request->input('doctor_id') ?: \Auth::id();

        return Schedule::where(compact('doctor_id'))->whereTimeBetween($times)->get();
    }

    /**
     * 排班时获取全部可选择的诊室
     */
    public function rooms(Request $request)
    {
        $this->doctorOrHospitalValidator($request);

        $input = $request->input();

        if (isset($input['hospital_id'])) {
            $room = Hospital::where('id', $input['hospital_id'])->select('room')->first();
        } else {
            $room = User::findOrFail($input['doctor_id'], ['id', 'hospital_id'])
                        ->hospital()->select('room')->first();
        }

        if (empty($room->room)) {
            return $this->hasNoData();
        }

        $rooms = [];

        for ($i = 1; $i <= $room->room; $i++) {
            $rooms[] = $i;
        }

        return $this->response($rooms);
    }

    public function store(ScheduleStoreRequest $request)
    {
        $input = $request->input();

        $where = $request->only(['doctor_id', 'time']);

        $result = Schedule::updateOrCreate($where, $input);

        return $this->response($result);
    }

    public function storeWithAMonth(Request $request)
    {
        $this->storeWithAMonthValidator($request);

        $time        = $request->input('time');
        $target      = $request->input('target');
        $target_time = Carbon::parse($target);

        $plan = $this->getSchedules($request, $this->monthWithTarget($time, $target));

        if (!$plan->count()) {
            return $this->setMessage('没有查询到上月的安排记录。')->hasNoData();
        }
        $doctor_id = $request->input('doctor_id') ?: \Auth::id();

        $exists = Schedule::where('doctor_id', $doctor_id)
                             ->whereTimeBetween($this->month($target))->count();

        if ($exists) {
            return $this->responseForDelete(false, '该月份已经设置了计划，全部删除后可进行保存。');
        }

        $result = new Collection();

        $plan->map(
            function (Schedule $schedule) use ($target_time, $result) {
                $schedule->time = $target_time->day(Carbon::parse($schedule->time)->day)->toDateString();

                $schedule = $schedule->toArray();
                Arr::forget($schedule, ['id', 'deleted_at']);
                $schedule['workload'] = json_encode($schedule['workload'], 256);
                $result->push($schedule);
            });

        \DB::table('st_reservation_schedules')->insert($result->toArray());

//        $plan = $this->getSchedules($request, $this->month($target));

        return $this->response($result->keyBy('time')->toArray());
    }

    public function update(ScheduleUpdateRequest $request)
    {
        $id = $request->input('id');

        $input = $request->except('id');

        $exists = Reservation::where(
            [
                'doctor_id' => $input['doctor_id'],
                'time'      => $input['time'],
            ]
        )->exists();

        if ($exists) {
            return $this->setCode(40002)->setMessage('取消当天预约后，在进行修改。')->response();
        }

        if (empty($input['workload'])) {
            return $this->destroy($request);
        }

        if ($id) {
            $schedule = Schedule::findOrFail($request->input('id'));
        } else {
            $schedule = Schedule::where(
                [
                    'doctor_id' => $input['doctor_id'],
                    'time'      => $input['time'],
                ]
            )->first();
            if (is_null($schedule)) {
                abort(404, '未找到相应数据，确认后再提交。');
            }
        }

        $result = $schedule->fill($input)->save();

        return $this->responseForUpdate($result, $schedule);
    }

    /**
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function destroy(Request $request)
    {
        $this->validate(
            $request,
            [
                'id'        => 'required_without:doctor_id,time|integer|min:1|exists:st_reservation_schedules',
                'doctor_id' => 'required_without:id|integer|min:1',
                'time'      => 'required_without:id|date',
            ]
        );

        $id = $request->input('id');

        if (empty($id)) {
            $schedule = Schedule::whereDoctorAndTime(
                $request->input('doctor_id'), $request->input('time')
            )->first();

            if (is_null($schedule)) {
                abort('422', '未找到相应的安排计划。');
            }
        } else {
            $schedule = Schedule::where('id', $id)->first();
        }

        if ($schedule->deleted_at) {
            return $this->setMessage('已删除过了，无需再次执行。')->setCode(10000)->response();
        }

        //  判断当天是否有预约，有预约不能删除
        $exists = Reservation::where(
            [
                'doctor_id' => $schedule->doctor_id,
                'time'      => $schedule->time,
            ]
        )->exists();

        if ($exists) {
            return $this->responseForDelete(false, '当天有进行预约患者，取消预约后再进行删除操作。');
        }

        return $this->responseForDelete($schedule->delete());
    }

    /**
     * 按月删除
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function destroyMonth(Request $request)
    {
        $this->validate($request, ['time' => 'required|date']);

        $time      = $request->input('time');
        $doctor_id = $request->input('doctor_id') ?: \Auth::id();
        $times     = $this->month($time);
        //
        $exists = Reservation::whereTimeBetween($times)
                   ->where('doctor_id', $doctor_id)
                   ->exists();

        if ($exists) {
            return $this->responseForDelete(false, '本月有进行预约患者，取消预约后再进行删除操作。');
        }

        $result = $this->destroyWithAMonth($doctor_id, $this->month($time));

        return $this->responseForDelete($result);
    }

    protected function destroyWithDoctorAndTime($doctor_id, $time)
    {
        return Schedule::where(compact('doctor_id', 'time'))->delete();
    }

    /**
     * 按月删除 排班
     * @param $doctor_id
     * @param $times
     * @return bool|mixed|null
     */
    protected function destroyWithAMonth($doctor_id, $times)
    {
        return Schedule::whereTimeBetween($times)
                       ->where('doctor_id', $doctor_id)
                       ->forceDelete();
    }

    protected function doctorOrHospitalValidator(Request $request)
    {
        $this->validate(
            $request,
            [
                'doctor_id'   => 'required_without:hospital_id|integer',
                'hospital_id' => 'required_without:doctor_id|integer',
            ],
            [
                'doctor_id.required_without'   => '医生ID和医院ID至少需要一项。',
                'doctor_id.integer'            => '医生ID 必须是一个数字。',
                'hospital_id.required_without' => '医生ID和医院ID至少需要一项。',
                'hospital_id.integer'          => '医院ID 必须是一个数字。',
            ]
        );
    }

    protected function storeWithAMonthValidator(Request $request)
    {
        $this->validate(
            $request,
            [
                'doctor_id' => 'integer',
                'time'      => 'required|date',
                'target'    => 'required|date',
            ],
            [
                'doctor_id.integer' => '医生ID 必须是一个数字。',
                'time.required'     => '时间 不能为空。',
                'time.date'         => '时间字段 必须是一个有效的日期。',
                'target.required'   => '目标时间 不能为空。',
                'target.date'       => '目标时间字段 必须是一个有效的日期。',
            ]
        );
    }

    protected function doctorAndTimeValidator(Request $request)
    {
        $this->validate(
            $request,
            [
                'doctor_id' => 'integer',
                'time'      => 'date',
            ],
            [
                'doctor_id.integer' => '医生ID 必须是一个数字。',
                'time.date'         => '时间字段 必须是一个有效的日期。',
            ]
        );
    }

    public function timesValidator(Request $request)
    {
        $this->validate(
            $request,
            [
                'times'   => 'required|array|size:2',
                'times.*' => 'required|date',
            ],
            [
                'times.required'   => '时间字段 不能为空。',
                'times.size'       => '时间字段 必须有一个开始时间，和一个结束时间。',
                'times.*.required' => '时间字段 不能为空。',
                'times.*.date'     => '时间字段 必须是一个有效的日期。',
            ]
        );
    }

    protected function month($time)
    {
        $time  = $time ? Carbon::parse($time) : Carbon::now();
        $start = $time->startOfMonth()->toDateString();
        $end   = $time->endOfMonth()->toDateString();

        return [$start, $end];
    }

    protected function monthWithTarget($time, $target)
    {
        $time   = Carbon::parse($time);
        $target = Carbon::parse($target);
        $end    = null;

        if ($time->daysInMonth > $target->daysInMonth) {
            $end = $time->startOfMonth()->day($target->daysInMonth)->toDateString();
        } else {
            $end = $time->endOfMonth()->toDateString();
        }

        return [$time->startOfMonth()->toDateString(), $end];
    }
}
